<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>deltamap--GUAN's Lab</title>
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>
</head>
<body>
    <div class="panel">
        <div id="title">Counter:</div>
        <div id="counter"></div>
    </div>
<!-- Generator: Adobe Illustrator 23.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
	width="300" height="150" viewBox="0 0 1920 1080" class="logo">
<g id="图层_2">
	<g>
		<g>
			<path class="st0" d="M1388.5,216.68c1.26-0.82,2.77-1.31,4.39-1.31h67.67c1.62,0,3.13,0.48,4.39,1.31l75.77-75.77
				c-29.35-29-69.68-46.91-114.2-46.91c-44.41,0-84.65,17.82-113.98,46.7L1388.5,216.68z"/>
		</g>
		<g>
			<path class="st1" d="M1543.37,143.6l-75.83,75.83c0.67,1.17,1.05,2.52,1.05,3.97v67.67c0,1.62-0.48,3.12-1.31,4.39l75.02,75.02
				c28.87-29.33,46.7-69.57,46.7-113.98C1589,212.64,1571.61,172.84,1543.37,143.6z"/>
		</g>
		<g>
			<path class="st2" d="M1464.52,298.06c-1.17,0.67-2.52,1.05-3.97,1.05h-67.67c-1.45,0-2.8-0.39-3.97-1.05l-75.31,75.31
				c29.24,28.25,69.03,45.63,112.9,45.63c43.97,0,83.86-17.47,113.11-45.85L1464.52,298.06z"/>
		</g>
		<g>
			<path class="st3" d="M1386.15,295.46c-0.82-1.26-1.31-2.77-1.31-4.39v-67.67c0-1.45,0.39-2.8,1.05-3.97l-76.05-76.05
				c-28.37,29.26-45.85,69.14-45.85,113.11c0,44.52,17.91,84.85,46.91,114.2L1386.15,295.46z"/>
		</g>
	</g>
	<g>
		<rect x="202.54" y="357.72" class="st4" width="1122.46" height="493.38"/>
		<text transform="matrix(1 0 0 1 202.5396 683.8184)"><tspan x="0" y="0" class="st3 st5 st6">G</tspan><tspan x="295.58" y="0" class="st2 st5 st6">U</tspan><tspan x="570" y="0" class="st0 st5 st6">A</tspan><tspan x="823.46" y="0" class="st1 st5 st6">N</tspan></text>
	</g>
</g>
<g id="图层_3">
	<rect x="348.06" y="737.15" class="st4" width="830.94" height="74.85"/>
	<text transform="matrix(1 0 0 1 348.0562 784.3447)" class="st7 st5 st8 st9">SYSTEM LABORATORY</text>
</g>
</svg>

    <script>
        //TODO:
        /* 变量的声明：
        dataOriginExample原数组，dataOrigin中间数组，dataOutput最终格式化的数组用于构造位置信息
        火柴的颜色比例尺：colorMatchASC增长 colorMAtchDESC下降
        delta增长的数组asc，delta降低的数组desc
         */
         /* 
         主要函数的声明
         format() 返回dataOutput
          */

        //定义example数组, 是数据规范，但是不用。
        const dataOriginExample = {
            node: [
                'one', 'two', 'three', 'four', 'five', 'six', "seven", "eight", 'nine', 'ten',
                'ele', 'twelve', 'thirt', 'fourt', 'fift', 'sixt', 'sevent', 'eightt', 'ninet', 'twent'
            ],
            link: [
                {
                    name: 'zhangsan',
                    start: 'four',
                    end: 'four'
                },
                {
                    name: 'lisi',
                    start: 'six',
                    end: 'three'
                },
                {
                    name: 'wangwu',
                    start: 'five',
                    end: 'one'
                },
                {
                    name: 'zhaoliu',
                    start: 'one',
                    end: 'two'
                },
                {
                    name: 'ruanqi',
                    start: 'two',
                    end: 'five'
                },
                {
                    name: 'zhangsan',
                    start: 'eight',
                    end: 'nine'
                },
                {
                    name: 'zhangsan',
                    start: 'ten',
                    end: 'six'
                },
                {
                    name: 'zhangsan',
                    start: 'thirt',
                    end: 'ele'
                },
                {
                    name: 'zhangsan',
                    start: 'sevent',
                    end: 'thirt'
                },
                {
                    name: 'zhangsan',
                    start: 'fift',
                    end: 'fift'
                },
                {
                    name: 'zhangsan',
                    start: 'ninet',
                    end: 'eightt'
                },
                {
                    name: 'zhangsan',
                    start: 'eightt',
                    end: 'ninet'
                },
                {
                    name: 'zhangsan',
                    start: 'fourt',
                    end: 'twent'
                },
                {
                    name: 'zhangsan',
                    start: 'nine',
                    end: 'eight'
                },
            ]
        }

        //定义原始数组
        let dataOrigin = {}
        dataOrigin.node = dataOriginExample.node
        //乱序数组
        let shuffleFrom = d3.shuffle([
                'one', 'two', 'three', 'four', 'five', 'six', "seven", "eight", 'nine', 'ten',
                'ele', 'twelve', 'thirt', 'fourt', 'fift', 'sixt', 'sevent', 'eightt', 'ninet', 'twent'
            ])
        let shuffleTo  = d3.shuffle([
                'one', 'two', 'three', 'four', 'five', 'six', "seven", "eight", 'nine', 'ten',
                'ele', 'twelve', 'thirt', 'fourt', 'fift', 'sixt', 'sevent', 'eightt', 'ninet', 'twent'
            ])

        dataOrigin.link = dataOrigin.node.map((d,i)=>{
            return {
                name: Math.random().toString(36).slice(-6),
                start: shuffleFrom[i],
                end: shuffleTo[i],
            }
        })


        //定义一些变量
        const width = 960,height = 600,
        outerRadius = 250,innerRadius = 175,
        textBias = 15

        //定义gui配置项
        const controls = new function(){
            this.innerRadius = innerRadius
            this.strokeWidth = 5
            this.showInnerNode = true
            this.colorAsc = [0, 83, 161, 0.87]
            this.colorDesc = [221, 41, 17, 0.76]
        }


        let scaleLinear = d3.scaleLinear()
        .domain([0, dataOrigin.node.length - 1])
        .range([-Math.PI/2, Math.PI/2])

        let dotSize = d3.scaleLinear()
        .domain([0, dataOrigin.node.length - 1])
        .range([13, 3])   
        
        let dataOutput = format(dataOrigin)
        asc = [], desc = []

        dataOutput.link.forEach(d=>{
            if(d.delta > 0) asc.push(d.delta);
            else desc.push(d.delta)
        })

        let colorAsc = 'rgba('.concat(controls.colorAsc.join(',')).concat(')')
        let colorAsc0 = 'rgba('+controls.colorAsc[0]+','+controls.colorAsc[1]+','+controls.colorAsc[2]+','+0.4+')'
        let colorDesc = 'rgba('.concat(controls.colorDesc.join(',')).concat(')')
        let colorDesc0 = 'rgba('+controls.colorDesc[0]+','+controls.colorDesc[1]+','+controls.colorDesc[2]+','+0.4+')'

        let colorMatchASC = d3.scaleSequential(d3.interpolateLab(colorAsc0, colorAsc))
        .domain(d3.extent(asc));
        let colorMatchDESC = d3.scaleSequential(d3.interpolateLab(colorDesc, colorDesc0))
        .domain(d3.extent(desc));


        // let colorMatchASC = d3.scaleSequential(d3.interpolateLab("rgb(141, 195, 245)","rgba(0, 83, 161, 0.87)"))
        // .domain(d3.extent(asc));

        // let colorMatchDESC = d3.scaleSequential(d3.interpolateLab("rgba(221, 41, 17, 0.76)", "rgba(255, 179, 169, 0.76) "))
        // .domain(d3.extent(desc));

        
        //计算交点函数
        
        // function equation(r, x1, y1, x2, y2){
            // let x = new Expression('x')

            // let y = new Expression('y')
            // // let t1 =   algebra.parse(`x-${x1}`).multiply(algebra.parse(`${y2-y1}`))
            // let eq = new Equation(algebra.parse(`y-${y1}`).multiply(algebra.parse(`${x2-x1}`)), algebra.parse(`x-${x1}`).multiply(algebra.parse(`${y2-y1}`)))
            // let y0 = eq.solveFor('y')

            // let eqn = new Equation(x.multiply(x).add(algebra.parse(`${y0}`).multiply(algebra.parse(`${y0}`))),radius*radius)
            // let solution = eqn.solveFor('x')
        //     let sol1 = nerdamer.solve(`${x2-x1}*(y-${y1}) = ${y2-y1}*(x-${x1})`,'y').toString().slice(1,-1)
        //     let solution = nerdamer.solve(`x^2+(${sol1})^2=${r}^2`,'x').toString().slice(1,-1).split(',')
        //     return solution.every((item)=>item<r&&item>0)&&solution.length>1&&solution[0]!==solution[1];
        // }
        
        //构造数学公式通过求公共解来计算交点的个数

        // let unionCounter = dataOutput.link.reduce((prev,cur,i)=>{
        //     if(equation(controls.innerRadius, outerRadius*Math.cos(dataOutput.node[cur.from].angle), outerRadius*Math.sin(dataOutput.node[cur.from].angle), controls.innerRadius*Math.cos(dataOutput.node[cur.to].angle), controls.innerRadius*Math.sin(dataOutput.node[cur.to].angle))){
        //         return prev+1;
        //     }else{
        //         return prev;
        //     }
        // },0)

        // let unionCounter = 0
        // dataOutput.link.forEach(cur=>{
        //     if(equation(controls.innerRadius, outerRadius*Math.cos(dataOutput.node[cur.from].angle), outerRadius*Math.sin(dataOutput.node[cur.from].angle), controls.innerRadius*Math.cos(dataOutput.node[cur.to].angle), controls.innerRadius*Math.sin(dataOutput.node[cur.to].angle))){
        //         unionCounter++
        //     }
        // })

        // console.log('   unionCounter', unionCounter)


        //定义画布
        const svg = d3.select('body')
        .append('svg')
        .attr('width', width)
        .attr('height', height)
        .append('g')
        .attr('transform', `translate(${width/2},${height/2})`)


        //画inner圆轨迹
        let arcInner = d3.arc()
        .innerRadius(innerRadius)
        .outerRadius(innerRadius)
        .startAngle(0)
        .endAngle(Math.PI)

        let arc = svg.append('g')
        .attr('class', 'orbit-inner')
        .append('path')
        .attr('d', arcInner)  


        //画Outer圆的轨迹
        let arcOuter = d3.arc()
        .innerRadius(outerRadius)
        .outerRadius(outerRadius)
        .startAngle(0)
        .endAngle(Math.PI)

        svg.append('g')
        .attr('class', 'orbit-outer')
        .append('path')
        .attr('d', arcOuter)  


        //画从原点出发的放射轨迹
        svg.append('g')
        .attr('class', 'radialLine')
        .selectAll('line')
        .data(dataOutput.node)
        .enter()
        .append('line')
        .attr('x1', 0)
        .attr('y1', 0)
        .attr('x2', (d, i)=>{
            const {angle} = d
            return outerRadius*Math.cos(angle)
        })
        .attr('y2', (d, i)=>{
            const {angle} = d
            return outerRadius*Math.sin(angle)
        })
        
        //定义tooltip的selection
        let div = d3.select('body')
        .append('div')
        .attr('class', 'tooltip')
        .style('opacity', 0);

        //计算切线长度，来判断线段的交点个数
        let tangent = Math.sqrt(outerRadius*outerRadius-controls.innerRadius*controls.innerRadius)
        let intersectCounter = 0

        //画layer之间的link
        let link = svg
        .append('g')
        .selectAll('line')
        .data(dataOutput.link)
        .enter()
        .append('line')
        .attr('class', 'link')
        .attr('x1', (d,i)=>{
            let angle = dataOutput.node[d.from].angle
            return outerRadius*Math.cos(angle)
        })
        .attr('x2', (d,i)=>{
            let angle = dataOutput.node[d.to].angle
            return innerRadius*Math.cos(angle)
        })
        .attr('y1', (d,i)=>{
            let angle = dataOutput.node[d.from].angle
            return outerRadius*Math.sin(angle)
        })
        .attr('y2',(d,i)=>{
            let angle = dataOutput.node[d.to].angle
            return innerRadius*Math.sin(angle)
        })
        .each(d=>{
            let angle = {
                x1:dataOutput.node[d.from].angle,
                x2:dataOutput.node[d.to].angle,
                y1:dataOutput.node[d.from].angle,
                y2:dataOutput.node[d.to].angle
            }
            if(Math.sqrt(Math.pow(outerRadius*Math.cos(angle.x1)-controls.innerRadius*Math.cos(angle.x2),2)+Math.pow(outerRadius*Math.sin(angle.y1)-controls.innerRadius*Math.sin(angle.y2),2))>tangent){
                intersectCounter++
            }
        })
        .attr('stroke', (d,i)=>d.delta>0? colorMatchASC(d.delta) : colorMatchDESC(d.delta))
        .attr('stroke-width', 2.5)
        .on('mouseover', mouseover)
        .on("mouseout", mouseout)

        document.getElementById('counter').innerHTML =intersectCounter


        //画D区域的link
        let clip = svg.append('defs')
        .append('clipPath')
        .attr('id', 'clip')
        .append('circle')
        .attr('r', innerRadius)

        let link_d = svg
        .append('g')
        .attr('clip-path', 'url(#clip)')
        .selectAll('line')
        .data(dataOutput.link)
        .enter()
        .append('line')
        .attr('class', 'link-d')
        .attr('x1', (d,i)=>{
            let angle = dataOutput.node[d.from].angle
            return outerRadius*Math.cos(angle)
        })
        .attr('x2', (d,i)=>{
            let angle = dataOutput.node[d.to].angle
            return innerRadius*Math.cos(angle)
        })
        .attr('y1', (d,i)=>{
            let angle = dataOutput.node[d.from].angle
            return outerRadius*Math.sin(angle)
        })
        .attr('y2',(d,i)=>{
            let angle = dataOutput.node[d.to].angle
            return innerRadius*Math.sin(angle)
        })
        .attr('stroke', (d,i)=>d.delta>0? colorMatchASC(d.delta) : colorMatchDESC(d.delta))
        .attr('stroke-width', controls.strokeWidth)
        .on('mouseover', mouseover_d)
        .on("mouseout", mouseout_d)


        //画Outer层layer的node
        svg.append('g')
        .selectAll('circle')
        .data(dataOutput.node)
        .enter()
        .append('circle')
        .attr('cx', d=>{
            return outerRadius*Math.cos(d.angle)
        })
        .attr('cy', d=>{
            return outerRadius*Math.sin(d.angle)
        })
        .attr('r', (d,i)=>{
            return dotSize(i)
        })
        .attr('fill', 'lightgray')
        // //画inner层layer的node
        let innerNode = svg.append('g')
        .selectAll('circle')
        .data(dataOutput.node)
        .enter()
        .append('circle')
        .attr('cx', d=>{
            return innerRadius*Math.cos(d.angle)
        })
        .attr('cy', d=>{
            return innerRadius*Math.sin(d.angle)
        })
        .attr('r', (d,i)=>{
            return dotSize(i)
        })
        .attr('fill', 'lightgray')


        //绘制node-outer的标签
        let text = svg.append('g')
        .attr('class', 'node-text-outer')
        .selectAll('.node-text-outer')
        .data(dataOutput.node)
        .enter()
        .append('text')
        .text(d=>d.rank)
        .attr('transform', d=>`translate(${(outerRadius+textBias)*Math.cos(d.angle)}, ${(outerRadius+textBias)*Math.sin(d.angle)})rotate(${(d.angle*180)/3.14})`)
        


        //构造gui
        let gui = new dat.GUI()

        gui.add(controls, 'innerRadius', controls.innerRadius-60, outerRadius).step(2).onChange(innerRadius=>{
            let tangent = Math.sqrt(outerRadius*outerRadius-innerRadius*innerRadius)
            let intersectCounter = 0
            //inner半圆
            arcInner.innerRadius(innerRadius)
            .outerRadius(innerRadius);
            arc.attr('d', arcInner)  
            //inner的node
            innerNode.attr('cx', d=>innerRadius*Math.cos(d.angle))
            .attr('cy', d=>innerRadius*Math.sin(d.angle))
            //内圆的link
            clip.attr('r', innerRadius);
            link_d
            .attr('x2', (d,i)=>{
                let angle = dataOutput.node[d.to].angle
                return innerRadius*Math.cos(angle)
            })
            .attr('y2',(d,i)=>{
                let angle = dataOutput.node[d.to].angle
                return innerRadius*Math.sin(angle)
                })
            //layer之间的link
            link
            .attr('x2', (d,i)=>{
                let angle = dataOutput.node[d.to].angle
                return innerRadius*Math.cos(angle)
            })
            .attr('y2',(d,i)=>{
                let angle = dataOutput.node[d.to].angle
                return innerRadius*Math.sin(angle)
            })
            .each(d=>{
                let angle = {
                    x1:dataOutput.node[d.from].angle,
                    x2:dataOutput.node[d.to].angle,
                    y1:dataOutput.node[d.from].angle,
                    y2:dataOutput.node[d.to].angle
                }
                if(Math.sqrt(Math.pow(outerRadius*Math.cos(angle.x1)-innerRadius*Math.cos(angle.x2),2)+Math.pow(outerRadius*Math.sin(angle.y1)-innerRadius*Math.sin(angle.y2),2))>tangent){
                    intersectCounter++;
                }
            })
        document.getElementById('counter').innerHTML =intersectCounter

    })
        gui.add(controls, 'strokeWidth', 2.5, 6).name('Filering Width').onChange(strokeWidth=>{
            link_d.style('stroke-width', strokeWidth)
        })
        gui.add(controls, 'showInnerNode').name('Inner visible').onChange(bool=>{
            innerNode.style('display', bool?'block':'none')
        })
        gui.addColor(controls, 'colorAsc').name('Color of Asc').onChange(e=>{
            //修改上升的
            e = round(e)
            colorAsc = 'rgba('.concat(e.join(',')).concat(')')
            colorAsc0 = 'rgba('+e[0]+','+e[1]+','+e[2]+','+0.4+')'
            colorMatchASC = d3.scaleSequential(d3.interpolateLab(colorAsc0, colorAsc))
            .domain(d3.extent(asc));
            link_d.attr('stroke', d=>{
                return d.delta>0? colorMatchASC(d.delta) : colorMatchDESC(d.delta)
            })
            link.attr('stroke', d=>{
                return d.delta>0? colorMatchASC(d.delta) : colorMatchDESC(d.delta)
            })
        })
        gui.addColor(controls, 'colorDesc').name('Color of Desc').onChange(e=>{
            //修改上升的
            e = round(e)
            colorDesc = 'rgba('.concat(e.join(',')).concat(')')
            colorDesc0 = 'rgba('+e[0]+','+e[1]+','+e[2]+','+0.4+')'
            colorMatchDESC = d3.scaleSequential(d3.interpolateLab(colorDesc, colorDesc0))
            .domain(d3.extent(asc));

            link_d.attr('stroke', d=>{
                return d.delta>0? colorMatchASC(d.delta) : colorMatchDESC(d.delta)
            })
            link.attr('stroke', d=>{
                return d.delta>0? colorMatchASC(d.delta) : colorMatchDESC(d.delta)
            })
        })



        //对数组的元素进行四舍五入的函数
        function round(arr){
            return arr.map(d=>{
                return Math.round(d)
            })
        }
    //定义node位置构造函数
        //格式化原数组，使得成为可以进行位置计算的数组
        function format(example){
            let data = {}

            data.node = example.node.map((d, i)=>{
                return {
                    uid: i,
                    rank: d,
                    angle: scaleLinear(i),
                }
            })

            let fromArray = [], toArray = []
            data.link = example.link.map((d, i)=>{
                let from = d.start, to = d.end,


                fromArrayEle = data.node.findIndex((item)=>{
                    return item.rank == from
                })
                fromArray.push(fromArrayEle)

                toArrayEle = data.node.findIndex((item)=>{
                    return item.rank == to
                })
                toArray.push(toArrayEle)

                return {
                    uid: i,
                    name: d.name,
                    from: fromArray[i],
                    to: toArray[i],
                    fromName: d.start,
                    toName: d.end,
                    delta: fromArray[i] - toArray[i]
                }
            })
            return data;
        }


        //交互：tooltip  mouseover
        function mouseover(d,i){
            div.transition()
            .duration(170)
            .style('opacity', 0.9);
            div.html(`Name: ${d.name}</br>Period One: ${d.fromName}</br>Period Two: ${d.toName}</br>delta: ${d.delta}`)
            .style('left', (d3.event.pageX) + "px")
            .style("top", (d3.event.pageY - 28) + "px");
            link_d.style('display', 'none')
            
            // link_d.classed('link-d-hover',(item,i,nodes)=>{
            //     return d.name !== item.name
            // })
            // link.classed('link-hover',(item,i,nodes)=>{
            //     return d.name !== item.name
            // })
        }
        //交互：tooltip  mouseout
        function mouseout(d,i){
            div.transition()
            .duration(500)
            .style("opacity", 0);
            link_d.style('display', 'block')
        }
        function mouseover_d(d,i){
            div.transition()
            .duration(170)
            .style('opacity', 0.9);
            div.html(`Name: ${d.name}</br>Period One: ${d.fromName}</br>Period Two: ${d.toName}`)
            .style('left', (d3.event.pageX) + "px")
            .style("top", (d3.event.pageY - 28) + "px");
            link_d.style('display', 'none')
            }
        function mouseout_d(d,i){
            div.transition()
            .duration(500)
            .style("opacity", 0);
            link_d.style('display', 'block')
            }

        console.log('dataOrigin', dataOrigin)

        console.log('dataOutput', dataOutput)
    </script>
</body>
<style>
    .orbit-inner{
        stroke: gray;
        /* stroke-dasharray: 4px 4px; */
        stroke-width: 1px;
        opacity: 0.5; 
        
    }
    .orbit-outer{
        stroke: gray;
        /* stroke-dasharray: 4px 4px; */
        stroke-width: .8px;
        opacity: 0.5; 
    }
    .link{
        opacity: 0.5;
        stroke-linecap: round;
        transition: all 0.3s ease;
    }
    .link-D{
        stroke-linecap: round;
    }
    .radialLine{
        stroke: gray;
        stroke-dasharray: 4px 6px;
        stroke-width: .8px;
        opacity: 0.5; 
    }
    .node-text-outer{
        font: 300 .8em  "Helvetica Neue", Helvetica, Arial, sans-serif;
        fill: #bbb;
        text-anchor: start;
    }
    
    .link-target{
        display: none;
    }
    .tooltip{
        position: absolute;
        padding: 10px;
        font: 12px sans-serif;
        background: lightsteelblue;
        border: 0px;
        border-radius: 8px;
        pointer-events: none;
        text-align: left;
        line-height: 1.5em;
        font-weight: bold;
    }
    .link:hover{
        stroke-width: 6px;
        opacity: 1;
    }
/* 修改gui的样式 */
.dg .c .slider-fg {
    background: #d6642f;
}
.dg .cr.number input[type=text] {
    color: #d6642f;
}
.panel{
    position: absolute;
    right: 0;
    top:180px;
    min-width:300px;
    text-align: center;
    font: 4em  "Helvetica Neue", Helvetica, Arial, sans-serif;
    color: rgb(139, 139, 139);
    line-height: 1.5em;
}
#counter{
    font-size: 2.4em;
}
.st0{fill:#FFBC4A;}
.st1{fill:#31B729;}
.st2{fill:#FF5D5B;}
.st3{fill:#5171FE;}
.st4{fill:none;}
.st5{font-family:'ArialMT';}
.st6{font-size:380px;}
.st7{fill:#BFBFBF;}
.st8{font-size:55px;}
.st9{letter-spacing:12;}
.logo{
    position: absolute;
    bottom: 0;
    left:0;
}
</style>
</html>